package hk.edu.cityu.is.riskmgmt.algorithm.pricing;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

import java.io.FileReader;

import org.apache.commons.math.linear.RealMatrix;
import org.apache.commons.math.linear.RealMatrixImpl;
import org.apache.commons.math.stat.correlation.Covariance;
import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.ParseDouble;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.prefs.CsvPreference;

public class CAPM {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(CAPM.class);

	/**
	 * @param args
	 */
	private double rf;

	private int stocknum = 3;
	private int days = 60;

	private int alldays = 61;
	private double[] beta = new double[stocknum];
	private double[][] prices = new double[stocknum][alldays];
	private double[][] stockReturn = new double[stocknum][alldays];
	private double[] marketReturn = new double[alldays];
	private double[] stdev = new double[stocknum];
	private double[] var = new double[stocknum];
	private double[] cov = new double[stocknum];
	private double[] lastPrice = new double[stocknum];
	private double mktStdev;
	private double mktVar;
	private RealMatrix covarianceMatrix;
	private double[] market = new double[alldays];
	private static final CellProcessor[] userProcessors = new CellProcessor[] {
			new Optional(new ParseDouble()), new Optional(new ParseDouble()),
			new Optional(new ParseDouble()), new Optional(new ParseDouble()) };

	private void setLastPrices(int day) {
		this.lastPrice = new RealMatrixImpl(this.prices).transpose()
				.getRow(day);
		// System.out.println(this.lastPrice[0]);

	}

	private void readFromCSV() {

		try {
			ICsvBeanReader inFile = new CsvBeanReader(new FileReader(
					"d:\\prices.csv"), CsvPreference.EXCEL_PREFERENCE);
			final String[] header = inFile.getCSVHeader(true);
			StockPrice sp;
			int idx = 0;
			while ((sp = inFile.read(StockPrice.class, header, userProcessors)) != null) {
				if (idx >= alldays) {
					break;
				}
				market[idx] = sp.getPrice1();
				prices[0][idx] = sp.getPrice2();
				prices[1][idx] = sp.getPrice3();
				prices[2][idx] = sp.getPrice4();
				idx++;
			}
		} catch (Exception e) {
			logger.error("readFromCSV()", e); //$NON-NLS-1$
		}

	}

	public void setBeta(double[] beta) {
		this.beta = beta;
	}

	public void getBeta() {
		for (int idx = 0; idx < stocknum; idx++) {
			beta[idx] = cov[idx] / mktVar;
			if (logger.isInfoEnabled()) {
				logger.info("getBeta() - " + cov[idx] + ", " + mktVar); //$NON-NLS-1$ //$NON-NLS-2$
			}
			if (logger.isInfoEnabled()) {
				logger.info("getBeta() - beta is: " + beta[idx]); //$NON-NLS-1$
			}
		}
	}

	public void setRf(double rf) {
		this.rf = rf;
	}

	public double[] getCAPM() {
		double capm[] = new double[stocknum];
		for (int idx = 0; idx < stocknum; idx++) {
			capm[idx] = rf + beta[idx] * (marketReturn[idx] - rf);
			if (logger.isInfoEnabled()) {
				logger.info("getCAPM() - CAPM: " + capm[idx]); //$NON-NLS-1$
			}
		}
		return capm;
	}

	private void getReturns() {
		for (int idx1 = 0; idx1 < this.stocknum; idx1++) {
			for (int idx2 = 0; idx2 < days; idx2++) {
				this.stockReturn[idx1][idx2] = Math.log(this.prices[idx1][idx2]
						/ this.prices[idx1][idx2 + 1]);

			}
			for (int idx3 = 0; idx3 < days; idx3++) {
				this.marketReturn[idx3] = Math.log(this.market[idx3]
						/ this.market[idx3 + 1]);
			}
		}
	}

	private void getStdev() {
		StandardDeviation sd = new StandardDeviation();
		for (int idx = 0; idx < stocknum; idx++) {
			this.stdev[idx] = sd.evaluate(this.stockReturn[idx]);
			// System.out.println("Stdev: " + this.stdev[idx]);
		}
	}

	private void getVar() {
		for (int idx = 0; idx < stocknum; idx++) {
			this.var[idx] = this.stdev[idx] * this.stdev[idx];
		}
	}

	private void getMktStdev() {
		StandardDeviation sd = new StandardDeviation();
		this.mktStdev = sd.evaluate(this.marketReturn);
	}

	private void getMktVar() {
		this.mktVar = this.mktStdev * this.mktStdev;
		if (logger.isInfoEnabled()) {
			logger.info("getMktVar() - mkt var is: " + mktVar); //$NON-NLS-1$
		}
	}

	private void getCov() {
		Covariance cova = new Covariance();
		for (int idx = 0; idx < stocknum; idx++) {
			cov[idx] = cova.covariance(marketReturn, stockReturn[idx]);
			if (logger.isInfoEnabled()) {
				logger.info("getCov() - cov is: " + cov[idx]); //$NON-NLS-1$
			}

		}
	}

	public double[] getPrice(double res[]) {
		double prices[] = new double[stocknum];
		for (int idx = 0; idx < stocknum; idx++) {
			prices[idx] = Math.exp(res[idx]) * lastPrice[idx];
		}
		return prices;
	}

	public double[] getNext() {
		double[] allResults = new double[stocknum];
		// generateRandomPrices();
		readFromCSV();
		getReturns();
		getStdev();
		getVar();
		getCov();
		this.getMktStdev();
		this.getMktVar();
		this.getBeta();
		double res[] = this.getCAPM();
		this.setLastPrices(60);
		return this.getPrice(res);
	}

	public double[] simulateWithInput(int times) {

		double[] allResults = new double[stocknum];
		// generateRandomPrices();
		readFromCSV();
		getReturns();
		getStdev();
		getVar();
		getCov();
		this.getMktStdev();
		this.getMktVar();
		this.getBeta();
		double res[] = this.getCAPM();
		this.setLastPrices(60);
		return this.getPrice(res);

	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BasicConfigurator.configure();
		CAPM c = new CAPM();
		c.setRf(0.55);
		double[] res = new double[3];
		res = c.getNext();
		for (int idx = 0; idx < 3; idx++) {
			if (logger.isInfoEnabled()) {
				logger.info("main(String[]) - " + res[idx]); //$NON-NLS-1$
			}

		}

	}

}
